{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "(train_data, _), (test_data, _) =  tf.keras.datasets.mnist.load_data()\n",
    "train_data = train_data/np.float32(255)\n",
    "train_data = np.reshape(train_data, (train_data.shape[0], 784))\n",
    "\n",
    "\n",
    "test_data = test_data/np.float32(255)\n",
    "test_data = np.reshape(test_data, (test_data.shape[0], 784))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Class that defines the behavior of the RBM\n",
    "class RBM(object):\n",
    "    \n",
    "    def __init__(self, input_size, output_size, lr=1.0, batchsize=100):\n",
    "        \"\"\"\n",
    "        m: Number of neurons in visible layer\n",
    "        n: number of neurons in hidden layer\n",
    "        \"\"\"\n",
    "        #Defining the hyperparameters\n",
    "        self._input_size = input_size #Size of Visible\n",
    "        self._output_size = output_size #Size of outp\n",
    "        self.learning_rate = lr #The step used in gradient descent\n",
    "        self.batchsize = batchsize #The size of how much data will be used for training per sub iteration\n",
    "        \n",
    "        #Initializing weights and biases as matrices full of zeroes\n",
    "        self.w = tf.zeros([input_size, output_size], np.float32) #Creates and initializes the weights with 0\n",
    "        self.hb = tf.zeros([output_size], np.float32) #Creates and initializes the hidden biases with 0\n",
    "        self.vb = tf.zeros([input_size], np.float32) #Creates and initializes the visible biases with 0\n",
    "\n",
    "\n",
    "    #Forward Pass\n",
    "    def prob_h_given_v(self, visible, w, hb):\n",
    "        #Sigmoid \n",
    "        return tf.nn.sigmoid(tf.matmul(visible, w) + hb)\n",
    "\n",
    "    #Backward Pass\n",
    "    def prob_v_given_h(self, hidden, w, vb):\n",
    "        return tf.nn.sigmoid(tf.matmul(hidden, tf.transpose(w)) + vb)\n",
    "    \n",
    "    #Generate the sample probability\n",
    "    def sample_prob(self, probs):\n",
    "        return tf.nn.relu(tf.sign(probs - tf.random.uniform(tf.shape(probs))))\n",
    "\n",
    "    #Training method for the model\n",
    "    def train(self, X, epochs=10):\n",
    "               \n",
    "        loss = []\n",
    "        for epoch in range(epochs):\n",
    "            #For each step/batch\n",
    "            for start, end in zip(range(0, len(X), self.batchsize),range(self.batchsize,len(X), self.batchsize)):\n",
    "                batch = X[start:end]\n",
    "                    \n",
    "                #Initialize with sample probabilities\n",
    "                    \n",
    "                h0 = self.sample_prob(self.prob_h_given_v(batch, self.w, self.hb))\n",
    "                v1 = self.sample_prob(self.prob_v_given_h(h0, self.w, self.vb))\n",
    "                h1 = self.prob_h_given_v(v1, self.w, self.hb)\n",
    "                    \n",
    "                #Create the Gradients\n",
    "                positive_grad = tf.matmul(tf.transpose(batch), h0)\n",
    "                negative_grad = tf.matmul(tf.transpose(v1), h1)\n",
    "                    \n",
    "                #Update learning rates \n",
    "                self.w = self.w + self.learning_rate *(positive_grad - negative_grad) / tf.dtypes.cast(tf.shape(batch)[0],tf.float32)\n",
    "                self.vb = self.vb +  self.learning_rate * tf.reduce_mean(batch - v1, 0)\n",
    "                self.hb = self.hb +  self.learning_rate * tf.reduce_mean(h0 - h1, 0)\n",
    "                    \n",
    "            #Find the error rate\n",
    "            err = tf.reduce_mean(tf.square(batch - v1))\n",
    "            print ('Epoch: %d' % epoch,'reconstruction error: %f' % err)\n",
    "            loss.append(err)\n",
    "                    \n",
    "        return loss\n",
    "        \n",
    "    #Create expected output for our DBN\n",
    "    def rbm_output(self, X):\n",
    "        out = tf.nn.sigmoid(tf.matmul(X, self.w) + self.hb)\n",
    "        return out\n",
    "    \n",
    "    def rbm_reconstruct(self,X):\n",
    "        h = tf.nn.sigmoid(tf.matmul(X, self.w) + self.hb)\n",
    "        reconstruct = tf.nn.sigmoid(tf.matmul(h, tf.transpose(self.w)) + self.vb)\n",
    "        return reconstruct\n",
    "            \n",
    "            "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 0 reconstruction error: 0.058346\n",
      "Epoch: 1 reconstruction error: 0.053077\n",
      "Epoch: 2 reconstruction error: 0.050471\n",
      "Epoch: 3 reconstruction error: 0.047577\n",
      "Epoch: 4 reconstruction error: 0.047246\n",
      "Epoch: 5 reconstruction error: 0.045349\n",
      "Epoch: 6 reconstruction error: 0.046128\n",
      "Epoch: 7 reconstruction error: 0.043719\n",
      "Epoch: 8 reconstruction error: 0.044629\n",
      "Epoch: 9 reconstruction error: 0.042792\n",
      "Epoch: 10 reconstruction error: 0.043694\n",
      "Epoch: 11 reconstruction error: 0.043396\n",
      "Epoch: 12 reconstruction error: 0.041716\n",
      "Epoch: 13 reconstruction error: 0.043921\n",
      "Epoch: 14 reconstruction error: 0.041899\n",
      "Epoch: 15 reconstruction error: 0.043255\n",
      "Epoch: 16 reconstruction error: 0.042263\n",
      "Epoch: 17 reconstruction error: 0.041181\n",
      "Epoch: 18 reconstruction error: 0.041547\n",
      "Epoch: 19 reconstruction error: 0.040591\n",
      "Epoch: 20 reconstruction error: 0.040817\n",
      "Epoch: 21 reconstruction error: 0.042179\n",
      "Epoch: 22 reconstruction error: 0.041643\n",
      "Epoch: 23 reconstruction error: 0.040972\n",
      "Epoch: 24 reconstruction error: 0.040443\n",
      "Epoch: 25 reconstruction error: 0.041524\n",
      "Epoch: 26 reconstruction error: 0.040908\n",
      "Epoch: 27 reconstruction error: 0.040389\n",
      "Epoch: 28 reconstruction error: 0.040650\n",
      "Epoch: 29 reconstruction error: 0.041425\n",
      "Epoch: 30 reconstruction error: 0.040191\n",
      "Epoch: 31 reconstruction error: 0.040303\n",
      "Epoch: 32 reconstruction error: 0.041072\n",
      "Epoch: 33 reconstruction error: 0.040791\n",
      "Epoch: 34 reconstruction error: 0.039652\n",
      "Epoch: 35 reconstruction error: 0.040287\n",
      "Epoch: 36 reconstruction error: 0.040006\n",
      "Epoch: 37 reconstruction error: 0.041456\n",
      "Epoch: 38 reconstruction error: 0.040201\n",
      "Epoch: 39 reconstruction error: 0.040411\n",
      "Epoch: 40 reconstruction error: 0.039634\n",
      "Epoch: 41 reconstruction error: 0.039881\n",
      "Epoch: 42 reconstruction error: 0.040136\n",
      "Epoch: 43 reconstruction error: 0.040026\n",
      "Epoch: 44 reconstruction error: 0.039949\n",
      "Epoch: 45 reconstruction error: 0.039199\n",
      "Epoch: 46 reconstruction error: 0.041087\n",
      "Epoch: 47 reconstruction error: 0.039012\n",
      "Epoch: 48 reconstruction error: 0.040457\n",
      "Epoch: 49 reconstruction error: 0.039672\n"
     ]
    }
   ],
   "source": [
    "#Size of inputs is the number of inputs in the training set\n",
    "input_size = train_data.shape[1]\n",
    "rbm = RBM(input_size, 200)\n",
    "\n",
    "err = rbm.train(train_data,50)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0, 0.5, 'cost')"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(err)\n",
    "plt.xlabel('epochs')\n",
    "plt.ylabel('cost')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1440x288 with 16 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "out = rbm.rbm_reconstruct(test_data)\n",
    "\n",
    "# Plotting original and reconstructed images\n",
    "row, col = 2, 8\n",
    "idx = np.random.randint(0, 100, row * col // 2)\n",
    "f, axarr = plt.subplots(row, col, sharex=True, sharey=True, figsize=(20,4))\n",
    "for fig, row in zip([test_data,out], axarr):\n",
    "    for i,ax in zip(idx,row):\n",
    "        ax.imshow(tf.reshape(fig[i],[28, 28]), cmap='Greys_r')\n",
    "        ax.get_xaxis().set_visible(False)\n",
    "        ax.get_yaxis().set_visible(False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:new_tf]",
   "language": "python",
   "name": "conda-env-new_tf-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
